#include  "lp_global_vars.h"

void compute_performance(int sd, short event, void* arg) {
	UNUSED_ARG(sd);
	UNUSED_ARG(event);
	UNUSED_ARG(arg);

	gettimeofday(&performance_end_time, NULL);
	gettimeofday(&endTime_Total, NULL);

	lp_interval_performance->interval_time = measure_time_difference(performance_end_time, performance_start_time);
	lp_interval_performance->total_time = measure_time_difference(endTime_Total, startTime_Total);
	assert(lp_interval_performance->interval_time != 0);

	lp_interval_performance->interval_delivery_throughput = calculate_throughput(sizeof(inst) * lp_interval_performance->interval_num_packets_delivered, lp_interval_performance->interval_time);
	lp_interval_performance->interval_sending_throughput = calculate_throughput(sizeof(inst) * lp_interval_performance->interval_num_packets_sent, lp_interval_performance->interval_time);
	lp_interval_performance->interval_latency = ((lp_interval_performance->interval_latency) / (double) lp_interval_performance->interval_num_packets_delivered);
	lp_calculate_performance_over_all_interval_uptonow();
	lp_store_performance_in_array(&lp_performances[0], &lp_next_performance_index, lp_interval_performance);

	lp_print_performance(lp_interval_performance);
	lp_nulify_performance(lp_interval_performance);

	if (lp_next_performance_index >= STOPPING_CONDITION) {

		lp_store_performance_in_file(lp_performance_file, lp_next_performance_index);
		free(data);
		exit(1);
	}
	gettimeofday(&performance_start_time, NULL);
	int status = evtimer_add(print_stat_timeout_eve, &tv1);
	assert(status >= 0);
}

double measure_time_difference(struct timeval now, struct timeval past) {
	struct timeval elapsed_interval;

	elapsed_interval.tv_sec = now.tv_sec - past.tv_sec;
	elapsed_interval.tv_usec = (now.tv_usec > past.tv_usec ? now.tv_usec - past.tv_usec : past.tv_usec - now.tv_usec);
	double msec_interval = 1000 * elapsed_interval.tv_sec + elapsed_interval.tv_usec / (double) 1000;

	return msec_interval;
}

double calculate_throughput(int bytes, double elapsed_time) {
	double thr = (bytes / (double) (1024 * 1024));
	thr = thr / (double) elapsed_time;
	thr = thr * 1000 * 8;
	return thr;
}

void lp_calculate_performance_over_all_interval_uptonow() {
	int i;
	double avg_t = 0, avg_l = 0, avg_s_t = 0, avg_e = 0;
	for (i = 0; i < lp_next_performance_index; i++) {
		avg_s_t += lp_performances[i].interval_sending_throughput;
		avg_t += lp_performances[i].interval_delivery_throughput;
		avg_l += lp_performances[i].interval_latency;
		avg_e += lp_performances[i].interval_num_entrance;
	}
	lp_interval_performance->total_sending_throughput = (lp_interval_performance->interval_sending_throughput + avg_s_t) / (double) (lp_next_performance_index + 1);
	lp_interval_performance->total_delivery_throughput = (lp_interval_performance->interval_delivery_throughput + avg_t) / (double) (lp_next_performance_index + 1);
	lp_interval_performance->total_latency = (lp_interval_performance->interval_latency + avg_l) / (double) (lp_next_performance_index + 1);
	lp_interval_performance->total_num_entrance = (lp_interval_performance->interval_num_entrance + avg_e) / (double) (lp_next_performance_index + 1);
}

void lp_print_performance(lp_performance *p) {
	printf("------------------------------------------------------------------------------------------------------------------\n");
	printf("TOTAL_TIME:   %f INTERVAL_TIME: %f  TICKS_PER_SECOND: %f\n\n", p->total_time, p->interval_time, p->interval_num_entrance);
	printf("INTERVAL DELIVERY: NUM_PACKETS: %d, THROUGHPUT: %f\n", p->interval_num_packets_delivered, p->interval_delivery_throughput);
	printf("INTERVAL SENT: 	   NUM_PACKETS: %d, THROUGHPUT: %f\n\n", p->interval_num_packets_sent, p->interval_sending_throughput);
	printf("TOTAL DELIVERY:    NUM_PACKETS: %d, THROUGHPUT: %f\n", p->total_num_packets_delivered, p->total_delivery_throughput);
	printf("TOTAL SENT:        NUM_PACKETS: %d, THROUGHPUT: %f\n\n", p->total_num_packets_proposed, p->total_sending_throughput);
	printf("INTERVAL 		   HOLES:       %d, TOTAL HOLES %d\n\n", p->total_num_holes, p->interval_num_holes);
	printf("INTERVAL REDUNDANT PACKETS:     %d  LOST_PACKETS: %d\n", p->interval_num_redundant_packets_received, p->interval_num_packets_lost);
	printf("TOTAL    REDUNDANT PACKETS:     %d\n\n", p->total_num_redundant_packets_received);
	printf("LATENCY: %f\n\n", p->interval_latency);
}

void lp_nulify_performance(lp_performance *p) {

	p->interval_time = 0;
	p->interval_num_packets_delivered = 0;
	p->interval_delivery_throughput = 0;
	p->interval_num_packets_sent = 0;
	p->interval_sending_throughput = 0;
	p->interval_num_holes = 0;
	p->interval_num_redundant_packets_received = 0;
	p->interval_num_packets_lost = 0;
	p->interval_num_entrance = 0;
	p->interval_latency = 0;
	p->interval_num_packets_received = 0;
	p->interval_num_packets_lost = 0;
	p->interval_num_overwritten_insts = 0;

	if (lp_next_performance_index == 0) {
		p->total_time = 0;
		p->total_num_packets_delivered = 0;
		p->total_delivery_throughput = 0;
		p->total_num_packets_proposed = 0;
		p->total_sending_throughput = 0;
		p->total_num_holes = 0;
		p->total_num_redundant_packets_received = 0;
		p->total_num_entrance = 0;
		p->total_latency = 0;
		p->total_num_packets_received = 0;
		p->interval_num_overwritten_insts = 0;

	}

}

void lp_store_performance_in_file(const char * performance_file, int num_entries) {
	FILE *performance_fp;
	int i;
	performance_fp = fopen(performance_file, "a");
	assert(performance_fp != NULL);

	for (i = 0; i < num_entries; i++) {
		//1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
		fprintf(performance_fp, "%d %d %d %f %f %d %f %d %f %d %f %d %f %d %d %d %d %f %d %f %d\n", i + 1, // 1 -
				SUBMIT_PER_WAKE, // 2-
				(int) tv2.tv_usec, // 3-
				lp_performances[i].total_time, // 4 -
				lp_performances[i].interval_time, // 5-
				lp_performances[i].interval_num_packets_delivered, // 6 -
				lp_performances[i].interval_delivery_throughput, // 7 -
				lp_performances[i].interval_num_packets_sent, // 8 -
				lp_performances[i].interval_sending_throughput, // 9 -
				lp_performances[i].total_num_packets_delivered, // 10 -
				lp_performances[i].total_delivery_throughput, // 11 -
				lp_performances[i].total_num_packets_proposed, // 12 -
				lp_performances[i].total_sending_throughput, // 13 -
				lp_performances[i].total_num_holes, // 14 -
				lp_performances[i].interval_num_holes, // 15 -
				lp_performances[i].total_num_redundant_packets_received,// 16 -
				lp_performances[i].interval_num_redundant_packets_received,// 17 -
				lp_performances[i].interval_num_entrance, // 18 -
				lp_performances[i].highest_deliverable_inst_id, // 19 -
				lp_performances[i].interval_latency, // 20 -
				lp_performances[i].interval_num_packets_lost); // 21 -
	}

	fclose(performance_fp);
}

void lp_store_performance_in_array(lp_performance * performances_array, int * performance_index, lp_performance *i_p) {

	memcpy((performances_array + *performance_index), i_p, sizeof(lp_performance));
	*(performance_index) = *(performance_index) + 1;
}

